package com.example.ethereum.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

@Slf4j
@Service
public class ContractCompilerService {

    private static final String CONTRACT_NAME = "ERC20Token";
    private static final String CONTRACT_FILE = "contracts/ERC20Token.sol";
    private static final String BUILD_DIR = "build/contracts";

    @Value("${solc.path:}")
    private String solcPath;

    /**
     * 编译Solidity合约
     * @return 编译后的字节码
     * @throws Exception 编译失败时抛出异常
     */
    public String compileContract() throws Exception {
        log.info("Starting contract compilation...");

        // 检查solc是否可用
        String solcExecutable = getSolcExecutable();
        if (solcExecutable == null) {
            throw new RuntimeException(
                "Solidity compiler (solc) is not available.\n" +
                "Please either:\n" +
                "1. Install solc and add it to system PATH, or\n" +
                "2. Download solc executable and configure 'solc.path' in application.properties\n" +
                "   Download from: https://github.com/ethereum/solidity/releases"
            );
        }

        // 获取合约源文件路径
        String contractPath = getContractSourcePath();
        if (contractPath == null) {
            throw new RuntimeException("Contract source file not found: " + CONTRACT_FILE);
        }

        // 创建构建目录
        createBuildDirectory();

        // 编译合约
        String binFilePath = compileWithSolc(contractPath, solcExecutable);

        // 读取编译后的字节码
        String bytecode = readBytecode(binFilePath);

        log.info("Contract compiled successfully. Bytecode length: {}", bytecode.length());
        return bytecode;
    }

    /**
     * 获取solc可执行文件路径
     */
    private String getSolcExecutable() {
        // 如果配置了路径，使用配置的路径
        if (solcPath != null && !solcPath.trim().isEmpty()) {
            File solcFile = new File(solcPath);
            if (solcFile.exists() && solcFile.canExecute()) {
                log.info("Using configured solc path: {}", solcPath);
                return solcPath;
            } else {
                log.warn("Configured solc path does not exist or is not executable: {}", solcPath);
            }
        }

        // 尝试从系统PATH查找solc
        String os = System.getProperty("os.name").toLowerCase();
        String solcCommand = os.contains("win") ? "solc.exe" : "solc";
        
        // 先尝试直接使用命令名（从PATH查找）
        if (isCommandAvailable(solcCommand)) {
            log.info("Using solc from system PATH: {}", solcCommand);
            return solcCommand;
        }

        // 如果找不到，返回null
        return null;
    }

    /**
     * 检查命令是否可用
     */
    private boolean isCommandAvailable(String command) {
        try {
            ProcessBuilder processBuilder = new ProcessBuilder(command, "--version");
            processBuilder.redirectErrorStream(true);
            processBuilder.redirectErrorStream(true);
            Process process = processBuilder.start();
            
            // 读取输出
            BufferedReader reader = new BufferedReader(
                new InputStreamReader(process.getInputStream())
            );
            String line;
            StringBuilder output = new StringBuilder();
            while ((line = reader.readLine()) != null) {
                output.append(line).append("\n");
            }
            
            int exitCode = process.waitFor();
            if (exitCode == 0) {
                log.info("Solc version: {}", output.toString().trim());
                return true;
            }
        } catch (Exception e) {
            // 命令不可用
        }
        return false;
    }

    /**
     * 获取合约源文件路径
     */
    private String getContractSourcePath() {
        try {
            ClassPathResource resource = new ClassPathResource(CONTRACT_FILE);
            if (resource.exists()) {
                // 获取文件的绝对路径
                File file = resource.getFile();
                return file.getAbsolutePath();
            }
        } catch (Exception e) {
            log.warn("Could not get contract file path: {}", e.getMessage());
        }
        return null;
    }

    /**
     * 创建构建目录
     */
    private void createBuildDirectory() throws IOException {
        Path buildPath = Paths.get(BUILD_DIR);
        if (!Files.exists(buildPath)) {
            Files.createDirectories(buildPath);
            log.info("Created build directory: {}", BUILD_DIR);
        }
    }

    /**
     * 使用solc编译合约
     */
    private String compileWithSolc(String contractPath, String solcExecutable) throws Exception {
        log.info("Compiling contract: {} using solc: {}", contractPath, solcExecutable);

        // 构建solc命令
        ProcessBuilder processBuilder = new ProcessBuilder(
            solcExecutable,
            "--bin",
            "--abi",
            contractPath,
            "-o",
            BUILD_DIR
        );

        // 设置工作目录为项目根目录
        File projectRoot = new File(System.getProperty("user.dir"));
        processBuilder.directory(projectRoot);
        processBuilder.redirectErrorStream(true);

        // 执行编译
        Process process = processBuilder.start();

        // 读取输出
        BufferedReader reader = new BufferedReader(
            new InputStreamReader(process.getInputStream())
        );
        StringBuilder output = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            output.append(line).append("\n");
            log.debug("Solc output: {}", line);
        }

        int exitCode = process.waitFor();

        if (exitCode != 0) {
            log.error("Contract compilation failed. Exit code: {}", exitCode);
            log.error("Solc output: {}", output.toString());
            throw new RuntimeException(
                "Contract compilation failed: " + output.toString()
            );
        }

        // 检查bin文件是否生成
        String binFilePath = BUILD_DIR + "/" + CONTRACT_NAME + ".bin";
        File binFile = new File(projectRoot, binFilePath);
        if (!binFile.exists()) {
            throw new RuntimeException(
                "Compilation succeeded but bin file not found: " + binFilePath
            );
        }

        log.info("Contract compiled successfully. Bin file: {}", binFile.getAbsolutePath());
        return binFile.getAbsolutePath();
    }

    /**
     * 读取字节码文件
     */
    private String readBytecode(String binFilePath) throws IOException {
        File binFile = new File(binFilePath);
        if (!binFile.exists()) {
            throw new FileNotFoundException("Bin file not found: " + binFilePath);
        }

        // 读取文件内容
        byte[] bytes = Files.readAllBytes(binFile.toPath());
        String bytecode = new String(bytes, "UTF-8");

        // 清理字节码（移除换行符和空格）
        bytecode = bytecode.replaceAll("\\s+", "").trim();

        // 确保有0x前缀
        if (!bytecode.startsWith("0x")) {
            bytecode = "0x" + bytecode;
        }

        return bytecode;
    }

    /**
     * 检查是否已有编译好的字节码文件
     */
    public boolean hasCompiledBytecode() {
        try {
            ClassPathResource resource = new ClassPathResource("contracts/" + CONTRACT_NAME + ".bin");
            return resource.exists();
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 获取已编译的字节码（如果存在）
     */
    public String getCompiledBytecode() throws IOException {
        ClassPathResource resource = new ClassPathResource("contracts/" + CONTRACT_NAME + ".bin");
        if (resource.exists()) {
            InputStream inputStream = resource.getInputStream();
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            byte[] data = new byte[1024];
            int nRead;
            while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
                buffer.write(data, 0, nRead);
            }
            buffer.flush();
            String bytecode = new String(buffer.toByteArray(), "UTF-8");
            inputStream.close();
            
            // 清理字节码
            bytecode = bytecode.replaceAll("\\s+", "").trim();
            return bytecode.startsWith("0x") ? bytecode : "0x" + bytecode;
        }
        return null;
    }
}

